'''
access binary data in a structured way

This module implements "foreign data interface" for MicroPython.

The idea behind it is similar to CPython’s ctypes modules, but the actual API
is different, streamlined and optimized for small size.

The basic idea of the module is to define data structure layout with about the
same power as the C language allows, and then access it using familiar dot-syntax
to reference sub-fields.

[View Doc](https://docs.micropython.org/en/latest/library/uctypes.html)
'''
# Constants
# Integer types for structure descriptors.
# Constants for 8, 16, 32, and 64 bit types are provided, both signed and unsigned.
UINT8: int = ...
INT8: int = ...
UINT16: int = ...
INT16: int = ...
UINT32: int = ...
INT32: int = ...
UINT64: int = ...
INT64: int = ...

# Floating-point types for structure descriptors.
FLOAT32: int = ...
FLOAT64: int = ...

BFUINT8: int = ...
BFINT8: int = ...
BFUINT16: int = ...
BFINT16: int = ...
BFUINT32: int = ...
BFINT32: int = ...
BF_POS: int = ...
BF_LEN: int = ...
SHORT: int = ...
USHORT: int = ...
INT: int = ...
UINT: int = ...
LONG: int = ...
ULONG: int = ...
LONGLONG: int = ...
ULONGLONG: int = ...

VOID: int = ...
'''
VOID is an alias for `UINT8`, and is provided to conveniently define C’s void
pointers: (`uctypes.PTR`, `uctypes.VOID`).
'''

# Type constants for pointers and arrays.
# Note that there is no explicit constant for structures, it’s implicit: an
# aggregate type without PTR or ARRAY flags is a structure.
PTR: int = ...
ARRAY: int = ...

LITTLE_ENDIAN: int = ...
'''
Layout type for a little-endian packed structure.

Packed means that every field occupies exactly as many bytes as defined in the
descriptor, i.e. the alignment is 1.
'''

BIG_ENDIAN: int = ...
'''Layout type for a big-endian packed structure.'''

NATIVE: int = ...
'''
Layout type for a native structure - with data endianness and alignment conforming
to the ABI of the system on which MicroPython runs.
'''


class struct(object):
	def __init__(self, addr: int, descriptor: dict, layout_type: int = NATIVE, /):
		'''
		Instantiate a "foreign data structure" object based on structure address
		in memory, descriptor (encoded as a dictionary), and layout type.
		'''


def sizeof(struct, layout_type: int = NATIVE, /) -> int:
	'''
	Return size of data structure in bytes.

	The `struct` argument can be either a structure class or a specific
	instantiated structure object (or its aggregate field).
	'''

def addressof(obj) -> int:
	'''
	Return address of an object.

	Argument should be bytes, bytearray or other object supporting buffer protocol
	(and address of this buffer is what actually returned).
	'''

def bytes_at(addr: int, size: int) -> bytes:
	'''
	Capture memory at the given address and size as bytes object.

	As bytes object is immutable, memory is actually duplicated and copied into
	bytes object, so if memory contents change later, created object retains
	original value.
	'''

def bytearray_at(addr: int, size: int) -> bytearray:
	'''
	Capture memory at the given address and size as bytearray object.

	Unlike `bytes_at()` function, memory is captured by reference, so it can be
	both written too, and you will access current value at the given memory address.
	'''
